<!DOCTYPE html>
<html>

  <head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  <title>安卓自定义View进阶-分类与流程</title>
  <meta name="description" content="进阶篇，主要讲述自定义view的分类，自定义View流程以及相关的一些重点，主要包括，自定义View，自定义ViewGroup。">
  <meta name="author" content="GcsSloop">
  <meta name="keywords" content="自定义View分类与流程, 构造函数, onMeasure, onSizeChanged, onLayout, onDraw, 自定义View详解, 自定义控件, 安卓, Android, CustomView, GcsSloop">
  <meta name="关键字" content="自定义View分类与流程, 构造函数, onMeasure, onSizeChanged, onLayout, onDraw, 自定义View详解, 自定义控件, 安卓, Android, CustomView, GcsSloop">
  

  <meta name="twitter:card" content="summary">
  <meta name="twitter:title" content="安卓自定义View进阶-分类与流程">
  <meta name="twitter:description" content="进阶篇，主要讲述自定义view的分类，自定义View流程以及相关的一些重点，主要包括，自定义View，自定义ViewGroup。">
  <meta name="twitter:keywords" content="自定义View分类与流程, 构造函数, onMeasure, onSizeChanged, onLayout, onDraw, 自定义View详解, 自定义控件, 安卓, Android, CustomView, GcsSloop">
  
  <meta property="og:type" content="article">
  <meta property="og:title" content="安卓自定义View进阶-分类与流程">
  <meta property="og:description" content="进阶篇，主要讲述自定义view的分类，自定义View流程以及相关的一些重点，主要包括，自定义View，自定义ViewGroup。">
  <meta name="og:keywords" content="自定义View分类与流程, 构造函数, onMeasure, onSizeChanged, onLayout, onDraw, 自定义View详解, 自定义控件, 安卓, Android, CustomView, GcsSloop">

  <meta name="theme-color" content="#343434">
  
  <link rel="icon" type="image/png" href="https://raw.githubusercontent.com/GcsSloop/gcssloop.github.io/master/assets/siteinfo/favicon.png" />
  <link href="https://raw.githubusercontent.com/GcsSloop/gcssloop.github.io/master/assets/siteinfo/favicon.png" rel="shortcut icon" type="image/png">
  
  <link href="//netdna.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet">
  <link rel="stylesheet" href="/css/main.css">

  <link rel="canonical" href="http://www.gcssloop.com/customview/CustomViewProcess">
  <link rel="alternate" type="application/rss+xml" title="GcsSloop" href="http://www.gcssloop.com/feed.xml">
  
  <meta name="google-site-verification" content="Z_g58PkzRAyBMxkqrcDdWrTBK8oOWM-7rUHauhLNF2E" />
  <meta name="baidu-site-verification" content="kUtTXCKaZs" />
  <meta name="baidu-site-verification" content="6DuDv3aaJX" />
  
  <!--阅读次数统计-->
  <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"> </script>
  
  <!--Fuck Weixin and Baidu-->
  <meta http-equiv="Cache-Control" content="no-transform">
  <meta http-equiv=”Cache-Control” content=”no-siteapp” />
  <meta name="applicable-device" content="pc,mobile">
  <meta name="HandheldFriendly" content="true"/>

  <!-- Google Ad -->
  <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
  <script>
    (adsbygoogle = window.adsbygoogle || []).push({
      google_ad_client: "ca-pub-2767831356529421",
      enable_page_level_ads: true
    });
  </script>

</head>


  <body>

    <span class="mobile btn-mobile-menu">
        <i class="fa fa-list btn-mobile-menu__icon"></i>
        <i class="fa fa-angle-up btn-mobile-close__icon hidden"></i>
    </span>
    
    <header class="panel-cover panel-cover--collapsed" style="background-image: url('/assets/siteinfo/background-cover.jpg')">
  <div class="panel-main">

    <div class="panel-main__inner panel-inverted">
    <div class="panel-main__content">

        <a href="/#blog" title="前往 GcsSloop 的主页" class="blog-button"><img src="/assets/siteinfo/avatar.jpg" width="80" alt="GcsSloop logo" class="panel-cover__logo logo" /></a>
        <h1 class="panel-cover__title panel-title"><a href="/#blog" title="link to homepage for GcsSloop" class="blog-button">GcsSloop</a></h1>

        
        <span class="panel-cover__subtitle panel-subtitle">Just do IT later.</span>
        
        <hr class="panel-cover__divider" />
        <p class="panel-cover__description">嗨，我是 GcsSloop，一名来自2.5次元的魔法师，Android自定义View系列文章作者，非著名程序员。</p>
        <hr class="panel-cover__divider panel-cover__divider--secondary" />
        
        
        <p class="panel-cover__description">欢迎来到我的魔法世界!</p>
        
        
        <div class="navigation-wrapper">
          <div>
            <nav class="cover-navigation cover-navigation--primary">
              <ul class="navigation">
                <li class="navigation__item"><a href="/#blog" title="访问博客" class="blog-button">博客</a></li>
                
                  
                    <li class="navigation__item"><a href="https://github.com/GcsSloop" target="_blank" title="GcsSloop's GitHub">GitHub</a></li>
                  
                  
                
                  
                    <li class="navigation__item"><a href="/timeline" title="博客目录">目录</a></li>
                  
                  
                
                  
                    <li class="navigation__item"><a href="https://xiaozhuanlan.com/u/GcsSloop" target="_blank" title="小专栏">专栏</a></li>
                  
                  
                
                  
                    <li class="navigation__item"><a href="/category/customview" title="自定义View教程目录">自定义控件</a></li>
                  
                  
                
                  
                    <li class="navigation__item"><a href="/friends" title="友链">友链</a></li>
                  
                  
                
              </ul>
            </nav>
          </div>
          
          <div><nav class="cover-navigation navigation--social">
  <ul class="navigation">

  
  <!-- Weibo -->
  <li class="navigation__item">
    <a href="http://weibo.com/GcsSloop" title="@GcsSloop 的微博" target="_blank">
      <i class='social fa fa-weibo'></i>
      <span class="label">Weibo</span>
    </a>
  </li>
  

  
  <!-- Github -->
  <li class="navigation__item">
    <a href="https://github.com/GcsSloop" title="@GcsSloop 的 Github" target="_blank">
      <i class='social fa fa-github'></i>
      <span class="label">Github</span>
    </a>
  </li>
  
  
  
  <!-- Twitter -->
  <li class="navigation__item">
    <a href="http://twitter.com/GcsSloop" title="@GcsSloop" target="_blank">
      <i class='social fa fa-twitter'></i>
      <span class="label">Twitter</span>
    </a>
  </li>
  

    

  

  
  <!-- RSS -->
  <li class="navigation__item">
    <a href="/feed.xml" rel="author" title="RSS" target="_blank">
      <i class='social fa fa-rss'></i>
      <span class="label">RSS</span>
    </a>
  </li>
  

  
  <!-- Email -->
  <li class="navigation__item">
    <a href="mailto:GcsSloop@gmail.com" title="发邮件给我">
      <i class='social fa fa-envelope'></i>
      <span class="label">Email</span>
    </a>
  </li>
  

  
  <!-- Copyright -->
  <li class="navigation__item">
    <a href="http://choosealicense.online" title="选择版权"  target="_blank">
      <i class="social fa fa-copyright"></i>
      <span class="label">版权</span>
    </a>
  </li>
  
  
  </ul>
</nav>
</div>
        </div>
      </div>
    </div>
    
    
    <div class="panel-cover--overlay cover-slate"></div>
    
  </div>
</header>


    <div class="content-wrapper">
        <div class="content-wrapper__inner">
            <article class="post-container post-container--single" itemscope itemtype="http://schema.org/BlogPosting">
  <header class="post-header">
    <div class="post-meta" style="font-size:.8em">
      <time datetime="2016-01-16 00:00:00 +0800" itemprop="datePublished" class="post-meta__date date">2016-01-16</time> &#8226; <span class="post-meta__tags tags">自定义View,自定义ViewGroup,流程</span> &#8226; View <span id="busuanzi_value_page_pv"></span> times.
</span>
    </div>
    <h1 class="post-title">安卓自定义View进阶-分类与流程</h1>
  </header>

  <section class="post">
    <p><strong>本章节为什么要叫进阶篇？(虽然讲的是基础内容)，因为从本篇开始，将会逐渐揭开自定义View的神秘面纱，每一篇都将比上一篇内容更加深入，利用所学的知识能够制作更加炫酷自定义View，就像在台阶上一样，每一篇都更上一层，<del>帮助大家一步步走向人生巅峰，出任CEO，迎娶白富美。</del> 误，是帮助大家更加了解那些炫酷的自定义View是如何制作的，达到举一反三的效果。</strong></p>

<p>自定义View绘制流程函数调用链(简化版)</p>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-71649.jpg?gcssloop" alt="自定义View绘制流程函数调用链" /></p>

<h2 id="一自定义view分类">一.自定义View分类</h2>

<p><strong>我将自定义View分为了两类(sloop个人分类法，非官方)：</strong></p>

<h3 id="1自定义viewgroup">1.自定义ViewGroup</h3>

<p><strong>自定义ViewGroup一般是利用现有的组件根据特定的布局方式来组成新的组件，大多继承自ViewGroup或各种Layout，包含有子View。</strong></p>

<blockquote>
  <p>例如：应用底部导航条中的条目，一般都是上面图标(ImageView)，下面文字(TextView)，那么这两个就可以用自定义ViewGroup组合成为一个Veiw，提供两个属性分别用来设置文字和图片，使用起来会更加方便。</p>
</blockquote>

<h3 id="2自定义view">2.自定义View</h3>

<p><strong>在没有现成的View，需要自己实现的时候，就使用自定义View，一般继承自View，SurfaceView或其他的View，不包含子View。</strong></p>

<blockquote>
  <p>例如：制作一个支持自动加载网络图片的ImageView，制作图表等。</p>
</blockquote>

<p><strong>PS： 自定义View在大多数情况下都有替代方案，利用图片或者组合动画来实现，但是使用后者可能会面临内存耗费过大，制作麻烦等诸多问题。</strong></p>

<hr />

<h2 id="二几个重要的函数">二.几个重要的函数</h2>

<h3 id="1构造函数">1.构造函数</h3>

<p>构造函数是View的入口，可以用于<strong>初始化一些的内容，和获取自定义属性</strong>。</p>

<p>View的构造函数有四种重载分别如下:</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">SloopView</span><span class="o">(</span><span class="n">Context</span> <span class="n">context</span><span class="o">)</span> <span class="o">{}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">SloopView</span><span class="o">(</span><span class="n">Context</span> <span class="n">context</span><span class="o">,</span> <span class="n">AttributeSet</span> <span class="n">attrs</span><span class="o">)</span> <span class="o">{}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">SloopView</span><span class="o">(</span><span class="n">Context</span> <span class="n">context</span><span class="o">,</span> <span class="n">AttributeSet</span> <span class="n">attrs</span><span class="o">,</span> <span class="kt">int</span> <span class="n">defStyleAttr</span><span class="o">)</span> <span class="o">{}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">SloopView</span><span class="o">(</span><span class="n">Context</span> <span class="n">context</span><span class="o">,</span> <span class="n">AttributeSet</span> <span class="n">attrs</span><span class="o">,</span> <span class="kt">int</span> <span class="n">defStyleAttr</span><span class="o">,</span> <span class="kt">int</span> <span class="n">defStyleRes</span><span class="o">)</span> <span class="o">{}</span>
</code></pre>
</div>

<p>可以看出，关于View构造函数的参数有多有少，先排除几个不常用的，留下常用的再研究。</p>

<p><strong>有四个参数的构造函数在API21的时候才添加上，暂不考虑。</strong></p>

<p>有三个参数的构造函数中第三个参数是默认的Style，这里的默认的Style是指它在当前Application或Activity所用的Theme中的默认Style，且只有在明确调用的时候才会生效，以系统中的ImageButton为例说明：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="kd">public</span> <span class="nf">ImageButton</span><span class="o">(</span><span class="n">Context</span> <span class="n">context</span><span class="o">,</span> <span class="n">AttributeSet</span> <span class="n">attrs</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">//调用了三个参数的构造函数，明确指定第三个参数</span>
    <span class="k">this</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="n">attrs</span><span class="o">,</span> <span class="n">com</span><span class="o">.</span><span class="na">android</span><span class="o">.</span><span class="na">internal</span><span class="o">.</span><span class="na">R</span><span class="o">.</span><span class="na">attr</span><span class="o">.</span><span class="na">imageButtonStyle</span><span class="o">);</span>
<span class="o">}</span>

<span class="kd">public</span> <span class="nf">ImageButton</span><span class="o">(</span><span class="n">Context</span> <span class="n">context</span><span class="o">,</span> <span class="n">AttributeSet</span> <span class="n">attrs</span><span class="o">,</span> <span class="kt">int</span> <span class="n">defStyleAttr</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">//此处调了四个参数的构造函数，无视即可</span>
    <span class="k">this</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="n">attrs</span><span class="o">,</span> <span class="n">defStyleAttr</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span> 
<span class="o">}</span>
</code></pre>
</div>

<p><strong>注意：即使你在View中使用了Style这个属性也不会调用三个参数的构造函数，所调用的依旧是两个参数的构造函数。</strong></p>

<p><strong>由于三个参数的构造函数第三个参数一般不用，暂不考虑，第三个参数的具体用法会在以后用到的时候详细介绍。</strong></p>

<p>排除了两个之后，只剩下一个参数和两个参数的构造函数，他们的详情如下：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="c1">//一般在直接New一个View的时候调用。</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">SloopView</span><span class="o">(</span><span class="n">Context</span> <span class="n">context</span><span class="o">)</span> <span class="o">{}</span>

<span class="c1">//一般在layout文件中使用的时候会调用，关于它的所有属性(包括自定义属性)都会包含在attrs中传递进来。</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">SloopView</span><span class="o">(</span><span class="n">Context</span> <span class="n">context</span><span class="o">,</span> <span class="n">AttributeSet</span> <span class="n">attrs</span><span class="o">)</span> <span class="o">{}</span>
</code></pre>
</div>

<p><strong>以下方法调用的是一个参数的构造函数：</strong></p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="c1">//在Avtivity中</span>
<span class="n">SloopView</span> <span class="n">view</span>  <span class="k">new</span> <span class="nf">SloopView</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
</code></pre>
</div>

<p><strong>以下方法调用的是两个参数的构造函数：</strong></p>

<div class="language-xml highlighter-rouge"><pre class="highlight"><code>//在layout文件中 - 格式为： 包名.View名
<span class="nt">&lt;com.sloop.study.SloopView</span>
  <span class="err">android:layout_width"wrap_content"</span>
  <span class="err">android:layout_height"wrap_content"</span><span class="nt">/&gt;</span>
</code></pre>
</div>

<p>关于构造函数先讲这么多，关于如何自定义属性和使用attrs中的内容，在后面会详细讲解，目前只需要知道这两个构造函数在何时调用即可。</p>

<h3 id="2测量view大小onmeasure">2.测量View大小(onMeasure)</h3>

<p><strong>Q: 为什么要测量View大小？</strong></p>

<p><strong>A: View的大小不仅由自身所决定，同时也会受到父控件的影响，为了我们的控件能更好的适应各种情况，一般会自己进行测量。</strong></p>

<p>测量View大小使用的是onMeasure函数，我们可以从onMeasure的两个参数中取出宽高的相关数据：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="nd">@Override</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onMeasure</span><span class="o">(</span><span class="kt">int</span> <span class="n">widthMeasureSpec</span><span class="o">,</span> <span class="kt">int</span> <span class="n">heightMeasureSpec</span><span class="o">)</span> <span class="o">{</span>
    <span class="kt">int</span> <span class="n">widthsize</span>  <span class="n">MeasureSpec</span><span class="o">.</span><span class="na">getSize</span><span class="o">(</span><span class="n">widthMeasureSpec</span><span class="o">);</span>      <span class="c1">//取出宽度的确切数值</span>
    <span class="kt">int</span> <span class="n">widthmode</span>  <span class="n">MeasureSpec</span><span class="o">.</span><span class="na">getMode</span><span class="o">(</span><span class="n">widthMeasureSpec</span><span class="o">);</span>      <span class="c1">//取出宽度的测量模式</span>
    
    <span class="kt">int</span> <span class="n">heightsize</span>  <span class="n">MeasureSpec</span><span class="o">.</span><span class="na">getSize</span><span class="o">(</span><span class="n">heightMeasureSpec</span><span class="o">);</span>    <span class="c1">//取出高度的确切数值</span>
    <span class="kt">int</span> <span class="n">heightmode</span>  <span class="n">MeasureSpec</span><span class="o">.</span><span class="na">getMode</span><span class="o">(</span><span class="n">heightMeasureSpec</span><span class="o">);</span>    <span class="c1">//取出高度的测量模式</span>
<span class="o">}</span>
</code></pre>
</div>

<p>从上面可以看出 onMeasure 函数中有 widthMeasureSpec 和 heightMeasureSpec 这两个 int 类型的参数， 毫无疑问他们是和宽高相关的， <strong>但它们其实不是宽和高， 而是由宽、高和各自方向上对应的测量模式来合成的一个值：</strong></p>

<p><strong>测量模式一共有三种， 被定义在 Android 中的 View 类的一个内部类View.MeasureSpec中：</strong></p>

<table>
  <thead>
    <tr>
      <th>模式</th>
      <th style="text-align: center">二进制数值</th>
      <th>描述</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>UNSPECIFIED</td>
      <td style="text-align: center">00</td>
      <td>默认值，父控件没有给子view任何限制，子View可以设置为任意大小。</td>
    </tr>
    <tr>
      <td>EXACTLY</td>
      <td style="text-align: center">01</td>
      <td>表示父控件已经确切的指定了子View的大小。</td>
    </tr>
    <tr>
      <td>AT_MOST</td>
      <td style="text-align: center">10</td>
      <td>表示子View具体大小没有尺寸限制，但是存在上限，上限一般为父View大小。</td>
    </tr>
  </tbody>
</table>

<p><strong>在int类型的32位二进制位中，31-30这两位表示测量模式,29~0这三十位表示宽和高的实际值，实际上如下：</strong></p>

<p>以数值1080(二进制为: 1111011000)为例(其中模式和实际数值是连在一起的，为了展示我将他们分开了)：</p>

<table>
  <thead>
    <tr>
      <th>模式名称</th>
      <th style="text-align: right">模式数值</th>
      <th>实际数值</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>UNSPECIFIED</td>
      <td style="text-align: right">00</td>
      <td>000000000000000000001111011000</td>
    </tr>
    <tr>
      <td>EXACTLY</td>
      <td style="text-align: right">01</td>
      <td>000000000000000000001111011000</td>
    </tr>
    <tr>
      <td>AT_MOST</td>
      <td style="text-align: right">10</td>
      <td>000000000000000000001111011000</td>
    </tr>
  </tbody>
</table>

<p><strong>PS: 实际上关于上面的东西了解即可，在实际运用之中只需要记住有三种模式，用 MeasureSpec 的 getSize是获取数值， getMode是获取模式即可。</strong></p>

<h4 id="注意">注意：</h4>

<p><strong>如果对View的宽高进行修改了，不要调用 super.onMeasure( widthMeasureSpec, heightMeasureSpec); 要调用 setMeasuredDimension( widthsize, heightsize); 这个函数。</strong></p>

<h3 id="3确定view大小onsizechanged">3.确定View大小(onSizeChanged)</h3>

<p>这个函数在视图大小发生改变时调用。</p>

<p><strong>Q: 在测量完View并使用setMeasuredDimension函数之后View的大小基本上已经确定了，那么为什么还要再次确定View的大小呢？</strong></p>

<p><strong>A: 这是因为View的大小不仅由View本身控制，而且受父控件的影响，所以我们在确定View大小的时候最好使用系统提供的onSizeChanged回调函数。</strong></p>

<p>onSizeChanged如下：</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="nd">@Override</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onSizeChanged</span><span class="o">(</span><span class="kt">int</span> <span class="n">w</span><span class="o">,</span> <span class="kt">int</span> <span class="n">h</span><span class="o">,</span> <span class="kt">int</span> <span class="n">oldw</span><span class="o">,</span> <span class="kt">int</span> <span class="n">oldh</span><span class="o">)</span> <span class="o">{</span>
    <span class="kd">super</span><span class="o">.</span><span class="na">onSizeChanged</span><span class="o">(</span><span class="n">w</span><span class="o">,</span> <span class="n">h</span><span class="o">,</span> <span class="n">oldw</span><span class="o">,</span> <span class="n">oldh</span><span class="o">);</span>
<span class="o">}</span>
</code></pre>
</div>

<p>可以看出，它又四个参数，分别为 宽度，高度，上一次宽度，上一次高度。</p>

<p>这个函数比较简单，我们只需关注 宽度(w), 高度(h) 即可，这两个参数就是View最终的大小。</p>

<h3 id="4确定子view布局位置onlayout">4.确定子View布局位置(onLayout)</h3>

<p><strong>确定布局的函数是onLayout，它用于确定子View的位置，在自定义ViewGroup中会用到，他调用的是子View的layout函数。</strong></p>

<p>在自定义ViewGroup中，onLayout一般是循环取出子View，然后经过计算得出各个子View位置的坐标值，然后用以下函数设置子View位置。</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="n">child</span><span class="o">.</span><span class="na">layout</span><span class="o">(</span><span class="n">l</span><span class="o">,</span> <span class="n">t</span><span class="o">,</span> <span class="n">r</span><span class="o">,</span> <span class="n">b</span><span class="o">);</span>
</code></pre>
</div>

<p>四个参数分别为：</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center">名称</th>
      <th style="text-align: center">说明</th>
      <th>对应的函数</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">l</td>
      <td style="text-align: center">View左侧距父View左侧的距离</td>
      <td>getLeft();</td>
    </tr>
    <tr>
      <td style="text-align: center">t</td>
      <td style="text-align: center">View顶部距父View顶部的距离</td>
      <td>getTop();</td>
    </tr>
    <tr>
      <td style="text-align: center">r</td>
      <td style="text-align: center">View右侧距父View左侧的距离</td>
      <td>getRight();</td>
    </tr>
    <tr>
      <td style="text-align: center">b</td>
      <td style="text-align: center">View底部距父View顶部的距离</td>
      <td>getBottom();</td>
    </tr>
  </tbody>
</table>

<p>具体可以参考 <a href="http://www.gcssloop.com/customview/CoordinateSystem/">坐标系</a> 这篇文章。</p>

<p><img src="http://gcsblog.oss-cn-shanghai.aliyuncs.com/blog/2019-04-29-071651.jpg?gcssloop" alt="View坐标系" /></p>

<p>PS：关于onLayout这个函数在讲解自定义ViewGroup的时候会详细讲解。</p>

<h3 id="5绘制内容ondraw">5.绘制内容(onDraw)</h3>

<p>onDraw是实际绘制的部分，也就是我们真正关心的部分，使用的是Canvas绘图。</p>

<div class="language-java highlighter-rouge"><pre class="highlight"><code><span class="nd">@Override</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onDraw</span><span class="o">(</span><span class="n">Canvas</span> <span class="n">canvas</span><span class="o">)</span> <span class="o">{</span>
    <span class="kd">super</span><span class="o">.</span><span class="na">onDraw</span><span class="o">(</span><span class="n">canvas</span><span class="o">);</span>
<span class="o">}</span>
</code></pre>
</div>

<p>关于Canvas绘图是本章节的重点，会分几篇文章进行详细讲解，敬请期待OwO。</p>

<h3 id="6对外提供操作方法和监听回调">6.对外提供操作方法和监听回调</h3>

<p>自定义完View之后，一般会对外暴露一些接口，用于控制View的状态等，或者监听View的变化.</p>

<p>本内容会在后续文章中以实例的方式进讲解。</p>

<hr />

<h2 id="三重点知识梳理">三.重点知识梳理</h2>

<h3 id="自定义view分类">自定义View分类</h3>

<blockquote>
  <p>PS ：实际上ViewGroup是View的一个子类。</p>
</blockquote>

<table>
  <thead>
    <tr>
      <th style="text-align: center">类别</th>
      <th>继承自</th>
      <th>特点</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">View</td>
      <td>View  SurfaceView 等</td>
      <td>不含子View</td>
    </tr>
    <tr>
      <td style="text-align: center">ViewGroup</td>
      <td>ViewGroup xxLayout等</td>
      <td>包含子View</td>
    </tr>
  </tbody>
</table>

<h3 id="自定义view流程">自定义View流程：</h3>

<table>
  <thead>
    <tr>
      <th style="text-align: center">步骤</th>
      <th style="text-align: center">关键字</th>
      <th>作用</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">1</td>
      <td style="text-align: center">构造函数</td>
      <td>View初始化</td>
    </tr>
    <tr>
      <td style="text-align: center">2</td>
      <td style="text-align: center">onMeasure</td>
      <td>测量View大小</td>
    </tr>
    <tr>
      <td style="text-align: center">3</td>
      <td style="text-align: center">onSizeChanged</td>
      <td>确定View大小</td>
    </tr>
    <tr>
      <td style="text-align: center">4</td>
      <td style="text-align: center">onLayout</td>
      <td>确定子View布局(自定义View包含子View时有用)</td>
    </tr>
    <tr>
      <td style="text-align: center">5</td>
      <td style="text-align: center">onDraw</td>
      <td>实际绘制内容</td>
    </tr>
    <tr>
      <td style="text-align: center">6</td>
      <td style="text-align: center">提供接口</td>
      <td>控制View或监听View某些状态。</td>
    </tr>
  </tbody>
</table>

<h2 id="about">About</h2>

<p><a href="http://www.gcssloop.com/customview/CustomViewIndex">本系列相关文章</a></p>

<p>作者微博: <a href="http://weibo.com/GcsSloop">GcsSloop</a></p>

<h2 id="参考资料">参考资料：</h2>

<p><a href="http://developer.android.com/reference/android/view/View.html">View</a><br />
<a href="http://developer.android.com/reference/android/view/ViewGroup.html">ViewGroup</a><br />
<a href="http://developer.android.com/reference/android/view/View.MeasureSpec.html">View.MeasureSpec</a><br />
<a href="http://blog.csdn.net/a396901990/article/details/36475213">onMeasure，MeasureSpec源码 流程 思路详解</a><br />
<a href="http://www.cnblogs.com/angeldevil/p/3479431.html">Android中自定义样式与View的构造函数中的第三个参数defStyle的意义</a> <br />
<a href="http://blog.csdn.net/z103594643/article/details/6755017">android view构造函数研究</a><br />
<a href="http://blog.csdn.net/mybeta/article/details/39993449">Android View构造方法第三参数使用方法详解</a><br />
<a href="http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1102/1891.html">Android 自定义View onMeasure方法的实现</a><br />
<a href="http://wangkuiwu.github.io/2014/06/20/View-OnMeasure/">Android API指南(二)自定义控件02之 onMeasure</a><br />
<a href="http://www.cnblogs.com/mengdd/p/3332882.html">Android中View的绘制过程 onMeasure方法简述</a><br />
<a href="http://www.gcssloop.com/customview/CoordinateSystem/">Android自定义View基础-坐标系</a><br /></p>

    <hr>
  </section>
</article>

<!--广告-->

<!--
<div>
<a href="https://m.aliyun.com/act/team1111/?spm=5176.11533457.1089570.5.424777e3AF8WOJ&userCode=hn5smxtw#/" target="_blank"><img src="/assets/aliyun/1111-980-120.jpg" width="100%"></a>
</div>
-->
<!--捐赠晶石-->
<section class="contribute">
    <script type="text/javascript"> 
      function toggle() {
        var con = document.getElementById("contribute");
        if (con.style.display == "none") {
          con.style.display = "block";
        } else {
          con.style.display = "none";
        }
      }
    </script> 
    <blockquote style="background-color:#F5F5F5; padding: 10px 20px 20px 10px; margin:0px" >
      <h4> 如果你觉得我的文章对你有帮助的话，欢迎赞助一些服务器费用! </h4>
      <p></p>
      <a id=“btn-toggle-contribute” class="btn-contribute" onclick="toggle()" >¥ 点击赞助</a>
      <br>
      <div id="contribute" style="display:none;">
        <p align="center" >
        <img src="/assets/images/wechat.png" alt="微信">
        <img src="/assets/images/alipay.png" alt="支付宝">
        </p>
        <p align="left" >
          <b>感谢所有支持我的魔法师，所有支持过我的魔法师都可以通过微信(GcsSloop)联系我，获赠我的付费专栏！</b>
          <!--
          <a href="/contribute">点击这里查看捐赠者名单。</a>
          -->
        </p>
      </div>
    </blockquote>
</section>
<div>
  <h2>欢迎关注我的微信公众号</h2>
  <img src="/assets/images/banner.jpg" width="100%">
</div>

<!--阅读更多-->
<section class="read-more">
  
  
  <div class="read-more-item">
    <span class="read-more-item-dim">最近的文章</span>
    <h2 class="post-list__post-title post-title"><a href="/customview/Canvas_BasicGraphics" title="link to 安卓自定义View进阶-Canvas之绘制图形">安卓自定义View进阶-Canvas之绘制图形</a></h2>
    <p class="excerpt">在上一篇自定义View分类与流程中我们了解自定义View相关的基本知识，不过，这些东西依旧还是理论，并不能拿来(zhuang)用(B), 这一次我们就了解一些能(zhaung)用(B)的东西。在...&hellip;</p>
    <div class="post-list__meta">
      <time datetime="2016-01-21 00:00:00 +0800" class="post-list__meta--date date">2016-01-21</time> &#8226; <span class="post-list__meta--tags tags">CustomView</span>
      <br/><br/>
      <a style="float:none; margin:0 auto;" class="btn-border-small" href=/customview/Canvas_BasicGraphics>继续阅读</a></div>
   </div>
   
   
   
   
   <div class="read-more-item">
       <span class="read-more-item-dim">更早的文章</span>
       <h2 class="post-list__post-title post-title"><a href="/customview/Color" title="link to 安卓自定义View基础-颜色">安卓自定义View基础-颜色</a></h2>
       <p class="excerpt">简要介绍安卓中的颜色相关内容，包括颜色的定义，创建颜色的几种方式，以及颜色的混合模式等。一.简单介绍颜色安卓支持的颜色模式：            颜色模式      备注           ...&hellip;</p>
       <div class="post-list__meta">
          <time datetime="2016-01-11 00:00:00 +0800" class="post-list__meta--date date">2016-01-11</time> &#8226; <span class="post-list__meta--tags tags">CustomView</span>
          <br/><br/>
          <a style="float:none; margin:0 auto;" class="btn-border-small" href=/customview/Color>继续阅读</a>
       </div>
   </div>
   
</section>

<!--网易云跟帖-->
<!--
<div id="cloud-tie-wrapper" class="cloud-tie-wrapper"></div>
<script src="https://img1.cache.netease.com/f2e/tie/yun/sdk/loader.js"></script>
<script>
var cloudTieConfig = {
  url: document.location.href, 
  sourceId: "",
  productKey: "a85dba2840134721a7b69a15b2e0f217",
  target: "cloud-tie-wrapper"
};
var yunManualLoad = true;
Tie.loader("aHR0cHM6Ly9hcGkuZ2VudGllLjE2My5jb20vcGMvbGl2ZXNjcmlwdC5odG1s", true);
</script>
-->

<style type="text/css">
.isso-comment > div.avatar {
    border: 0px;
    box-shadow: none;
    display: block;
    float: left;
    width: 7%;
    margin: 3px 15px 0 0;
}
.isso-postbox > .form-wrapper > .auth-section .post-action > input {
    border-radius: 6px;
    padding: 6px;
    padding-left: 16px;
    padding-right: 16px;
    border: 1px solid #CCC;
    background-color: #D58D44;
    cursor: pointer;
    outline: 0;
    color: #fff;
    size: 10;
    line-height: 1.4em;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.isso-postbox > .form-wrapper > .auth-section .post-action > input:hover {
    background-color: #272822;
}
.isso-postbox > .form-wrapper > .auth-section .post-action > input:active {
    background-color: #986530;
}
</style>

<section id="isso-thread"></section>

<script data-isso="//47.52.58.34:1234/"
        data-isso-css="true"
        data-isso-lang="zh"
        data-isso-reply-to-self="false"
        data-isso-require-author="false"
        data-isso-require-email="false"
        data-isso-max-comments-top="10"
        data-isso-max-comments-nested="5"
        data-isso-reveal-on-click="5"
        data-isso-avatar="true"
        data-isso-avatar-bg="#f0f0f0"
        data-isso-avatar-fg="#9abf88 #5698c4 #e279a3 #9163b6 ..."
        data-isso-vote="true"
        data-vote-levels=""
        src="//47.52.58.34:1234/js/embed.min.js">
        </script>

<!--
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
-->
<!-- OneV's Den -->
<!--
<ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-3324997515191619"
     data-ad-slot="9170309685"
     data-ad-format="auto"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
-->

            <section class="footer">
    <footer>
    	<span class="footer__copyright">本站点采用<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/deed.zh" target="_blank">知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议</a></span>
        <span class="footer__copyright">本站由 <a href="http://www.GcsSloop.com">@GcsSloop</a> 创建，采用 <a href="https://github.com/GcsSloop/Gcs-Vno-Jekyll" target="_blank">Gcs-Vno-Jekyll</a> 作为主题。<span id="busuanzi_container_site_pv"> 总访问量 <span id="busuanzi_value_site_pv"></span> 次</span> - &copy; 2019</span>
        <span class="footer__sitemap, footer__copyright"><a href="http://www.gcssloop.com/sitemap.xml" target="_blank">Site Map</a>
        <a href="http://www.gcssloop.com/vip" target="_blank">vip</a></span>
    </footer>
</section>

        </div>
    </div>
    
    <script type="text/javascript" src="//code.jquery.com/jquery-1.11.3.min.js"></script>

<script type="text/javascript" src="/js/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>

<script type="text/javascript" src="/js/main.js"></script>

<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-82493667-1', 'auto');
  ga('send', 'pageview');

</script>

    
  </body>

</html>
